package com.gitee.jmash.oidc.client.oauth;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.glassfish.jersey.internal.util.collection.MultivaluedStringMap;
import org.glassfish.jersey.media.multipart.FormDataMultiPart;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import jakarta.json.bind.Jsonb;
import jakarta.json.bind.JsonbBuilder;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.core.Form;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.core.Response;

/**
 * Restful Client Util.
 */
public class RestClientUtil {

  private static Log log = LogFactory.getLog(RestClientUtil.class);

  /**
   * 保存文件
   */
  protected static File saveFile(InputStream inputStream, File file) {
    try (FileOutputStream fos = new FileOutputStream(file)) {
      byte[] buffer = new byte[1024];
      int bytesRead;
      while ((bytesRead = inputStream.read(buffer)) != -1) {
        fos.write(buffer, 0, bytesRead);
      }
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
    return file;
  }


  /**
   * Get请求,返回Response.
   */
  protected static Response doGet(WebTarget target, String url, String... queryParams) {
    if (queryParams.length % 2 != 0) {
      throw new RuntimeException("动态参数必须为键值对!");
    }
    target = target.path(url);
    for (int i = 0; i < queryParams.length / 2; i++) {
      target = target.queryParam(queryParams[i * 2], queryParams[i * 2 + 1]);
    }
    log.debug("======" + target.getUri());
    Response resp = target.request(MediaType.APPLICATION_JSON).get();
    return resp;

  }

  /**
   * Get请求,返回实体.
   */
  public static <T> T doGet(String url, String path, Class<T> respClass, String... queryParams) {
    try (Client client = ClientBuilder.newClient(); Jsonb jsonb = JsonbBuilder.create()) {
      WebTarget target = client.target(url);
      Response resp = doGet(target, path, queryParams);
      if (resp.getStatus() != 200) {
        throw new RestClientException(resp);
      }
      String json = resp.readEntity(String.class);
      log.debug(json);
      T entity = jsonb.fromJson(json, respClass);
      return entity;
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }



  /**
   * Post请求,返回Response.
   */
  protected static <E> Response doPost(WebTarget target, String url, E req, String... queryParams) {

    if (queryParams.length % 2 != 0) {
      throw new RuntimeException("动态参数必须为键值对!");
    }
    target = target.path(url);
    for (int i = 0; i < queryParams.length / 2; i++) {
      target = target.queryParam(queryParams[i * 2], queryParams[i * 2 + 1]);
    }
    Entity<E> reqEntity = Entity.json(req);
    log.info(target);
    Response resp = target.request(MediaType.APPLICATION_JSON).post(reqEntity);
    return resp;

  }

  /**
   * Post请求,返回实体.
   */
  public static <E, F> F doPost(String url, String path, E req, Class<F> respClass,
      String... queryParams) {
    try (Client client = ClientBuilder.newClient(); Jsonb jsonb = JsonbBuilder.create()) {
      WebTarget target = client.target(url);
      Response resp = doPost(target, path, req, queryParams);
      if (resp.getStatus() != 200) {
        throw new RestClientException(resp);
      }
      String json = resp.readEntity(String.class);
      log.debug(json);
      F entity = jsonb.fromJson(json, respClass);
      return entity;
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * Post请求,返回Response.
   */
  protected static Response doPostForm(WebTarget target, String url, Form req,
      MultivaluedMap<String, Object> headers, String... queryParams) {

    if (queryParams.length % 2 != 0) {
      throw new RuntimeException("动态参数必须为键值对!");
    }
    target = target.path(url);
    for (int i = 0; i < queryParams.length / 2; i++) {
      target = target.queryParam(queryParams[i * 2], queryParams[i * 2 + 1]);
    }
    Entity<Form> reqEntity = Entity.form(req);
    log.info(target);
    Response resp =
        target.request(MediaType.APPLICATION_FORM_URLENCODED).headers(headers).post(reqEntity);
    return resp;

  }

  /**
   * Post请求,返回实体.
   */
  @SuppressWarnings({"unchecked", "rawtypes"})
  public static <F> F doPostForm(String url, String path, Form req, Class<F> respClass,
      String authorization, String... queryParams) {
    try (Client client = ClientBuilder.newClient(); Jsonb jsonb = JsonbBuilder.create()) {
      WebTarget target = client.target(url);
      MultivaluedMap<String, Object> headers = (MultivaluedMap)new MultivaluedStringMap();
      headers.putSingle("Authorization", authorization);
      Response resp = doPostForm(target, path, req, headers, queryParams);
      if (resp.getStatus() != 200) {
        throw new RestClientException(resp);
      }
      String json = resp.readEntity(String.class);
      log.debug(json);
      F entity = jsonb.fromJson(json, respClass);
      return entity;
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  /**
   * Post请求,返回Response. 支持表单数据和文件上传. 如果formData不为null，则使用它作为请求体；否则，使用JSON格式的req作为请求体.
   */
  protected static <E> Response doPostFile(WebTarget target, String url, FormDataMultiPart formData,
      String... queryParams) {
    if (queryParams.length % 2 != 0) {
      throw new RuntimeException("动态参数必须为键值对!");
    }
    target = target.path(url);
    for (int i = 0; i < queryParams.length / 2; i++) {
      target = target.queryParam(queryParams[i * 2], queryParams[i * 2 + 1]);
    }
    Entity<?> entity = Entity.entity(formData, formData.getMediaType());
    // 如果提供了formData，则使用它；否则，使用JSON格式的req。
    Response resp = target.request().post(entity);
    return resp;
  }

  /**
   * Post请求,返回实体.
   */
  public static <E, F> F doPostFile(String url, String path, FormDataMultiPart formData,
      Class<F> respClass, String... queryParams) {
    try (Client client = ClientBuilder.newBuilder().register(MultiPartFeature.class).build();
        Jsonb jsonb = JsonbBuilder.create()) {
      WebTarget target = client.target(url);
      Response resp = doPostFile(target, path, formData, queryParams);
      if (resp.getStatus() != 200) {
        throw new RestClientException(resp);
      }
      String json = resp.readEntity(String.class);
      log.debug(json);
      F entity = jsonb.fromJson(json, respClass);
      return entity;
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

}
